#!/usr/bin/env python3

# Copyright (c) 2020-2021 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


import os
import lz4.block
import json
import functools
from datetime import datetime


class Profile:

    def __init__(self, path):
        self._path = path
        self._recovery_file_path = os.path.join(self._path, "sessionstore-backups", "recovery.jsonlz4")

    @property
    def path(self):
        return self._path

    @property
    def paths(self):
        return [
            self._path,
            self._recovery_file_path,
        ]

    def get_recovery_data(self):
        try:
            with open(self._recovery_file_path, "rb") as f:
                # the first 8 bytes in recovery.jsonlz4 should contain
                # the string mozLz40
                assert f.read(8) == b"mozLz40\0"
                # after these 8 bytes the file is a lz4 stream
                compressed_data = f.read()
            data = lz4.block.decompress(compressed_data)
            return FirefoxRecoveryData(json.loads(data.decode("utf-8")))
        except FileNotFoundError:
            return None


class FirefoxRecoveryData:

    def __init__(self, data):
        self.data = data

    @property
    @functools.lru_cache(maxsize=1)
    def windows(self):
        windows = []
        for window in self.data["windows"]:
            window_tabs = []
            for tab in window["tabs"]:
                entry = tab["entries"][tab["index"] - 1]
                window_tabs.append(
                    FirefoxTab(
                        last_accessed=datetime.fromtimestamp(
                            tab["lastAccessed"] / 1000
                        ),
                        url=entry["url"],
                        title=entry["title"],
                    )
                )
            windows.append(
                FirefoxWindow(
                    tabs=window_tabs,
                    workspace=window.get("workspaceID", None),
                    zindex=window.get("zIndex", None),
                    size=(window.get("width", None), window.get("height", None)),
                    position=(window.get("screenX", None), window.get("screenY", None)),
                    mode=window.get("sizemode", None),
                )
            )
        return windows


class FirefoxTab:

    def __init__(self, url, title, last_accessed):
        self.url = url
        self.title = title
        self.last_accessed = last_accessed

    def to_json(self):
        return dict(
            last_accessed=self.last_accessed.isoformat(), url=self.url, title=self.title
        )


class FirefoxWindow:

    def __init__(self, tabs, workspace, zindex, size, position, mode):
        self.tabs = tabs
        self.workspace = workspace
        self.zindex = zindex
        self.size = size
        self.position = position
        self.mode = mode

    def to_json(self):
        return dict(
            tabs=[tab.to_json() for tab in self.tabs],
            workspace=self.workspace,
            zindex=self.zindex,
            size=self.size,
            position=self.position,
            mode=self.mode,
        )
